package sel.me.popular.library.learning.quasar;

import co.paralleluniverse.fibers.Fiber;
import co.paralleluniverse.fibers.SuspendExecution;
import co.paralleluniverse.strands.channels.Channel;

import static co.paralleluniverse.strands.channels.Channels.newChannel;

/**
 * 在github上无意中发现一个有趣的benchmark，大致是测试各种语言在生成百万actor/Fiber的开销skynet。
 *
 * 大致的逻辑是先生成10个Fiber，每个Fiber再生成10个Fiber，直到生成1百万个Fiber
 * ，然后每个Fiber做加法累积计算，并把结果发到channel里，这样一直递归到根Fiber。
 * 后将最终结果发到channel。如果逻辑没有错的话结果应该是499999500000。
 * 我们搞个Quasar版的，来测试一下性能。
 *
 *
 * @author shenenlu 2021年06月23日 下午17:59:05
 */
public class QuasarSkyNet1 {

    private static final int RUNS = 4;
    // = 0 unbufferd, > 0 buffered ; < 0 unlimited
    private static final int BUFFER = 1000;

    static void skynet(Channel<Long> c, long num, int size, int div) throws SuspendExecution, InterruptedException {
        if (size == 1) {
            c.send(num);
            return;
        }

        Channel<Long> rc = newChannel(BUFFER);
        long sum = 0L;
        for (int i = 0; i < div; i++) {
            long subNum = num + i * (size / div);
            new Fiber(() -> skynet(rc, subNum, size / div, div)).start();
        }
        for (int i = 0; i < div; i++)
            sum += rc.receive();
        c.send(sum);
    }

    public static void main(String[] args) throws Exception {
        //这里跑4次，是为了让JVM预热好做优化，所以我们以最后一个结果为准。
        for (int i = 0; i < RUNS; i++) {
            long start = System.nanoTime();

            Channel<Long> c = newChannel(BUFFER);
            new Fiber<>(() -> skynet(c, 0, 1_000_000, 10)).start();
            long result = c.receive();

            long elapsed = (System.nanoTime() - start) / 1_000_000;
            System.out.println((i + 1) + ": " + result + " (" + elapsed + " ms)");
        }
    }

}
